#pragma once

#include <iostream>
#include <memory>
#include <string>
#include <string.h>
#include <cerrno>
#include <strings.h>
#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace LogArea;

const static int defaultsockfd = -1;

// const static std::string defaultip = "127.0.0.1"; // 127.0.0.1 表示本地主机
const static uint16_t defaultport = -1;

class UdpServer
{
public:
    UdpServer(int port = defaultport) : 
        _sockfd(defaultsockfd)
        , _addr(port)
        , _isrunning(false)
    {}

    // 初始化服务器
    void InitServer()
    {
        // 创建socket
        _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            // 将错误信息格入日志中
            LOG(LogLevel::FATALER) << "socket: " << strerror(errno);
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket success, socketfd is: " << _sockfd;

        // 填充网络信息, 并绑定
        // 将 socket 信息填充入结构体内
        // struct sockaddr_in local;

        // bzero(&local, sizeof(local)); // 将结构体内信息清空
        // local.sin_family = AF_INET;
        // local.sin_port = ::htons(_port);                  // 将主机字节顺序转为网络字节顺序   网络中的字节顺序为大端模式
        // local.sin_addr.s_addr = ::inet_addr(_ip.c_str()); // 使用系统调用将ip地址转为4字节形式的 char.char.char.char

        
        // 将套接字与当前主机 IP 地址和端口号绑定, 通常用 sockaddr_in 结构来指定
        int n = ::bind(_sockfd, _addr.NetAddr(), _addr.SockLen()); 
        if (n < 0)
        {
            LOG(LogLevel::FATALER) << "bind: " << strerror(errno);
            Die(BIND_ERR);
        }

        LOG(LogLevel::INFO) << "bind success";
    }

    // 运行服务器
    void Start()
    {
        _isrunning = true;
        while (true)
        {
            // 输出消息内容 && 谁发给我的

            // peer 存储用户 sockaddr_in 的结构信息
            char inbuffer[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 服务器收取信息并作出反馈

            ssize_t n = ::recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, CONV(&peer), &len);
            if (n > 0)
            {
                inbuffer[n] = 0;
                InetAddr client(peer);

                std::string cliinfo = client.Ip() + ":" + std::to_string(client.Port()) + "# " + inbuffer;

                // 显示在服务器上的信息
                LOG(LogLevel::INFO) << cliinfo;

                // 回显到用户上的信息
                std::string echo_string = "echo# ";
                echo_string += cliinfo;
                ::sendto(_sockfd, echo_string.c_str(), sizeof(echo_string), 0, CONV(&peer), sizeof(peer));
            }
            _isrunning = false;
        }
    }

    ~UdpServer()
    {
        if (_sockfd != defaultsockfd)
            ::close(_sockfd);
    }

private:
    int _sockfd; // 套接字描述符

    InetAddr _addr;
    
    // std::string _ip; // 服务器ip地址
    // uint16_t _port;  // 端口号
    bool _isrunning; // 是否正在运行
};
